package com.amos.knife4j.service.impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.amos.knife4j.config.exception.BusinessException;
import com.amos.knife4j.config.result.ResultEnums;
import com.amos.knife4j.dao.BaseDao;
import com.amos.knife4j.service.BaseService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import tk.mybatis.mapper.entity.Example;
import tk.mybatis.mapper.entity.Example.Criteria;

import java.io.Serializable;
import java.util.List;
import java.util.Map;

/**
 * service基类实现
 *
 * @author meng_lbo
 * @date 2020/2/20  13:52
 */
@Service
public abstract class BaseServiceImpl<T, ID extends Serializable> implements BaseService<T, ID> {

    // 日志
    private static final Logger log = LoggerFactory.getLogger(BaseServiceImpl.class);
    @Autowired
    private BaseDao<T> baseDao;


    /**
     * 保存一个实体，null的属性也会保存，不会使用数据库默认值
     *
     * @param record
     * @return
     */
    @Override
    public int insert(T record) throws BusinessException {
        try {
            return baseDao.insert(record);
        } catch (Exception e) {
            // 记录service层异常，并包装抛出
            log.error(e.getMessage(), e);
            throw new BusinessException(ResultEnums.ERROR);
        }
    }

    /**
     * 保存一个实体，null的属性不会保存，会使用数据库默认值
     *
     * @param record
     * @return
     */
    @Override
    public int insertSelective(T record) throws BusinessException {
        try {
            return baseDao.insertSelective(record);
        } catch (Exception e) {
            // 记录service层异常，并包装抛出
            log.error(e.getMessage(), e);
            throw new BusinessException(ResultEnums.ERROR);
        }
    }

    /**
     * 根据主键更新实体全部字段，null值会被更新
     *
     * @param record
     * @return
     */
    /*@Override
    public int updateByPrimaryKey(T record) throws BusinessException {
        try {
            return baseDao.updateByPrimaryKey(record);
        } catch (Exception e) {
            // 记录service层异常，并包装抛出
            log.error(e.getMessage(), e);
            throw new BusinessException(ResultEnums.ERROR);
        }
    }*/

    /**
     * 根据主键更新属性不为null的值
     *
     * @param record
     * @return
     */
   /* @Override
    public int updateByPrimaryKeySelective(T record) throws BusinessException {
        try {
            return baseDao.updateByPrimaryKeySelective(record);
        } catch (Exception e) {
            // 记录service层异常，并包装抛出
            log.error(e.getMessage(), e);
            throw new BusinessException(ResultEnums.ERROR);
        }
    }*/

    /**
     * 根据主键字段进行删除，方法参数必须包含完整的主键属性
     *
     * @param id
     * @return
     */
    @Override
    public int deleteByPrimaryKey(ID id) throws BusinessException {
        try {
            return baseDao.deleteByPrimaryKey(id);
        } catch (Exception e) {
            // 记录service层异常，并包装抛出
            log.error(e.getMessage(), e);
            throw new BusinessException(ResultEnums.ERROR);
        }
    }

    /**
     * 根据主键字段进行查询，方法参数必须包含完整的主键属性，查询条件使用等号
     *
     * @param id
     * @return
     */
    @Override
    public T selectByPrimaryKey(ID id) throws BusinessException {
        try {
            return baseDao.selectByPrimaryKey(id);
        } catch (Exception e) {
            // 记录service层异常，并包装抛出
            log.error(e.getMessage(), e);
            throw new BusinessException(ResultEnums.ERROR);
        }
    }

    @Override
    public T selectOne(T record) throws BusinessException {
        try {
            return baseDao.selectOne(record);
        } catch (Exception e) {
            // 记录service层异常，并包装抛出
            log.error(e.getMessage(), e);
            throw new BusinessException(ResultEnums.ERROR);
        }
    }

    /**
     * 查询全部结果
     *
     * @return
     */
    @Override
    public List<T> selectAll() throws BusinessException {
        try {
            return baseDao.selectAll();
        } catch (Exception e) {
            // 记录service层异常，并包装抛出
            log.error(e.getMessage(), e);
            throw new BusinessException(ResultEnums.ERROR);
        }
    }

    /**
     * 根据实体中的属性值进行查询，查询条件使用等号
     *
     * @param record
     * @return
     */
    @Override
    public List<T> select(T record) throws BusinessException {
        try {
            return baseDao.select(record);
        } catch (Exception e) {
            // 记录service层异常，并包装抛出
            log.error(e.getMessage(), e);
            throw new BusinessException(ResultEnums.ERROR);
        }
    }

    /**
     * 根据实体中的属性查询总数，查询条件使用等号
     *
     * @param record
     * @return
     */
    @Override
    public int selectCount(T record) throws BusinessException {
        try {
            return baseDao.selectCount(record);
        } catch (Exception e) {
            // 记录service层异常，并包装抛出
            log.error(e.getMessage(), e);
            throw new BusinessException(ResultEnums.ERROR);
        }
    }

    /**
     * 相等查询和模糊查询, equels和like相同的类
     *
     * @param equels 相等条件，优先级高于like，不能为空
     * @param like   模糊条件，优先级低于equels，不能为空
     * @return
     * @throws BusinessException
     */
    @Override
    public List<T> select(T equels, T like) throws BusinessException {
        try {
            if (ObjectUtil.isNull(equels)) {
                throw new BusinessException(ResultEnums.FAILED.getCode(), "相等查询条件不能为空");
            }
            if (ObjectUtil.isNull(like)) {
                throw new BusinessException(ResultEnums.FAILED.getCode(), "模糊查询条件不能为空");
            }
            // 查询条件，注意，字段用实体类中的字段，不是表中的字段
            Example example = new Example(equels.getClass());
            Criteria criteria = example.createCriteria();
            // 将相等查询条件和模糊查询条件转为map
            Map<String, Object> equelsMap = BeanUtil.beanToMap(equels);
            Map<String, Object> likeMap = BeanUtil.beanToMap(like);
            // 存放value的对象
            Object value = null;
            // 遍历相等查询条件map
            for (String key : equelsMap.keySet()) {
                value = equelsMap.get(key);
                if (ObjectUtil.isNotNull(value) && StrUtil.isNotBlank(value.toString().trim())) {
                    // 移除模糊查询map中的字段
                    likeMap.remove(key);
                    // 拼接条件
                    criteria.andEqualTo(key, value.toString().trim());
                }
            }
            // 遍历模糊查询条件map
            for (String key : likeMap.keySet()) {
                value = likeMap.get(key);
                if (ObjectUtil.isNotNull(value) && StrUtil.isNotBlank(value.toString().trim())) {
                    // 组装模糊查询条件
                    criteria.andLike(key, "%" + value.toString().trim() + "%");
                }
            }
            // 分页查询
            List<T> list = baseDao.selectByExample(example);
            // 返回结果
            return list;
        } catch (BusinessException be) {
            // 业务异常直接抛出
            throw be;
        } catch (Exception e) {
            // 记录service层异常，并包装抛出
            log.error(e.getMessage(), e);
            throw new BusinessException(ResultEnums.ERROR);
        }
    }

    /**
     * 模糊查询
     *
     * @param like 模糊条件，不能为空
     * @return
     * @throws BusinessException
     */
    @Override
    public List<T> selectLike(T like) throws BusinessException {
        try {
            if (ObjectUtil.isNull(like)) {
                throw new BusinessException(ResultEnums.FAILED.getCode(), "模糊查询条件不能为空");
            }
            // 查询条件，注意，字段用实体类中的字段，不是表中的字段
            Example example = new Example(like.getClass());
            Criteria criteria = example.createCriteria();
            // 将模糊查询条件转为map
            Map<String, Object> likeMap = BeanUtil.beanToMap(like);
            // 存放value的对象
            Object value = null;
            // 遍历模糊查询条件map
            for (String key : likeMap.keySet()) {
                value = likeMap.get(key);
                if (ObjectUtil.isNotNull(value) && StrUtil.isNotBlank(value.toString().trim())) {
                    // 组装模糊查询条件
                    criteria.andLike(key, "%" + value.toString().trim() + "%");
                }
            }
            // 分页查询
            List<T> list = baseDao.selectByExample(example);
            // 返回结果
            return list;
        } catch (BusinessException be) {
            // 业务异常直接抛出
            throw be;
        } catch (Exception e) {
            // 记录service层异常，并包装抛出
            log.error(e.getMessage(), e);
            throw new BusinessException(ResultEnums.ERROR);
        }
    }

    /**
     * 分页查询
     *
     * @param pageSize 每页条数
     * @param pageNum  页码
     * @param orderBy  排序字段
     * @param isDesc   是否降序
     * @return
     */
    @Override
    public PageInfo<T> selectByPage(int pageSize, int pageNum, String orderBy, boolean isDesc)
            throws BusinessException {
        try {
            // 启动分页，组装排序
            startPage(pageSize, pageNum, orderBy, isDesc);
            // 查询
            List<T> list = select(null);
            // 组装分页对象并返回
            return new PageInfo<T>(list);
        } catch (BusinessException be) {
            // 业务异常直接抛出
            throw be;
        } catch (Exception e) {
            // 记录service层异常，并包装抛出
            log.error(e.getMessage(), e);
            throw new BusinessException(ResultEnums.ERROR);
        }
    }

    /**
     * 分页查询，equels和like相同的类
     *
     * @param equels   相等条件，优先级高于like，不能为空
     * @param like     模糊条件，优先级低于equels，不能为空
     * @param pageSize 每页条数
     * @param pageNum  页码
     * @param orderBy  排序字段
     * @param isDesc   是否降序
     * @return
     */
    @Override
    public PageInfo<T> selectByPage(T equels, T like, int pageSize, int pageNum, String orderBy, boolean isDesc)
            throws BusinessException {
        try {
            if (ObjectUtil.isNull(equels)) {
                throw new BusinessException(ResultEnums.FAILED.getCode(), "相等查询条件不能为空");
            }
            if (ObjectUtil.isNull(like)) {
                throw new BusinessException(ResultEnums.FAILED.getCode(), "模糊查询条件不能为空");
            }
            // 启动分页，组装排序
            startPage(pageSize, pageNum, orderBy, isDesc);
            // 查询
            List<T> list = select(equels, like);
            // 组装分页对象并返回
            return new PageInfo<T>(list);
        } catch (BusinessException be) {
            // 业务异常直接抛出
            throw be;
        } catch (Exception e) {
            // 记录service层异常，并包装抛出
            log.error(e.getMessage(), e);
            throw new BusinessException(ResultEnums.ERROR);
        }
    }

    /**
     * 分页查询 (相等查询)
     *
     * @param equels   相等条件，不能为空
     * @param pageSize 每页条数
     * @param pageNum  页码
     * @param orderBy  排序字段
     * @param isDesc   是否降序
     * @return
     * @throws BusinessException
     */
    @Override
    public PageInfo<T> selectByPage(T equels, int pageSize, int pageNum, String orderBy, boolean isDesc)
            throws BusinessException {
        try {
            if (ObjectUtil.isNull(equels)) {
                throw new BusinessException(ResultEnums.FAILED.getCode(), "相等查询条件不能为空");
            }
            // 启动分页，组装排序
            startPage(pageSize, pageNum, orderBy, isDesc);
            // 查询
            List<T> list = select(equels);
            // 组装分页对象并返回
            return new PageInfo<T>(list);
        } catch (BusinessException be) {
            // 业务异常直接抛出
            throw be;
        } catch (Exception e) {
            // 记录service层异常，并包装抛出
            log.error(e.getMessage(), e);
            throw new BusinessException(ResultEnums.ERROR);
        }
    }

    /**
     * 分页查询，equels和like相同的类
     *
     * @param pageSize 每页条数
     * @param pageNum  页码
     * @param like     模糊条件，优先级低于equels，不能为空
     * @param orderBy  排序字段
     * @param isDesc   是否降序
     * @return
     */
    @Override
    public PageInfo<T> selectByPage(int pageSize, int pageNum, T like, String orderBy, boolean isDesc)
            throws BusinessException {
        try {
            if (ObjectUtil.isNull(like)) {
                throw new BusinessException(ResultEnums.FAILED.getCode(), "模糊查询条件不能为空");
            }
            // 启动分页，组装排序
            startPage(pageSize, pageNum, orderBy, isDesc);
            // 查询
            List<T> list = selectLike(like);
            // 组装分页对象并返回
            return new PageInfo<T>(list);
        } catch (BusinessException be) {
            // 业务异常直接抛出
            throw be;
        } catch (Exception e) {
            // 记录service层异常，并包装抛出
            log.error(e.getMessage(), e);
            throw new BusinessException(ResultEnums.ERROR);
        }
    }

    /**
     * pagehelper 分页 排序组装
     *
     * @param pageSize
     * @param pageNum
     * @param orderBy
     * @param isDesc
     */
    private void startPage(int pageSize, int pageNum, String orderBy, boolean isDesc) {
        try {
            // 判断是否有排序字段
            if (StrUtil.isNotBlank(orderBy)) {
                // 判断是否降序
                if (isDesc) {
                    // 启动分页，降序
                    PageHelper.startPage(pageNum, pageSize, orderBy + " DESC");
                } else {
                    // 启动分页，升序
                    PageHelper.startPage(pageNum, pageSize, orderBy);
                }
            } else {
                // 启动分页，默认排序
                PageHelper.startPage(pageNum, pageSize);
            }
        } catch (Exception e) {
            // 记录service层异常，并包装抛出
            log.error(e.getMessage(), e);
            throw new BusinessException(ResultEnums.ERROR);
        }
    }

    @Override
    public int delete(T record) throws BusinessException {
        try {
            return baseDao.delete(record);
        } catch (Exception e) {
            // 记录service层异常，并包装抛出
            log.error(e.getMessage(), e);
            throw new BusinessException(ResultEnums.ERROR);
        }
    }
}
